/* Pontificia Universidad Javeriana - Facultad de Ingenier�a   */
package BESA.SystemLevel;

/* Proyecto Arquitectura SMA - Plataforma BESA - 2002          */
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Set;

import BESA.Agent.AgentBESA;
import BESA.Agent.StateBESA;
import BESA.Agent.StructBESA;
import BESA.DistributedBESA.DistributedBESAInit;
import BESA.DistributedBESA.Directory.AdmRemoteHandler;
import BESA.Exception.ExceptionBESA;
import BESA.Exception.ExceptionBESA_AgentNotFound;
import BESA.Exception.ExceptionBESA_FatalError;
import BESA.SystemLevel.Directory.AdmHandler;
import BESA.SystemLevel.Directory.AgHandler;
import BESA.SystemLevel.Directory.AgHandlerBESA;
import BESA.SystemLevel.Directory.Service;
import BESA.SystemLevel.Directory.greenpages.GreenPagesHelper;
import BESA.SystemLevel.transactions.gca.ClientGCA;
import BESA.SystemLevel.transactions.gca.GCA;
import BESA.SystemLevel.transactions.gca.GCAUtilities;
import BESA.SystemLevel.transactions.gcc.GCC_Impl;
import BESA.util.ConfigBESA;
import BESA.util.ReportBESA;
import BESA.util.VisualSetUp;

import java.util.logging.Level;
import java.util.logging.Logger;

import sample.test.cc.cool.gui.controllers.ClientController;

/**
 * AdmBESA constructora de un admnistrador local BESA
 * 
 * @param admName
 *            nombre/alias del administrador local
 * @param ipAddress
 *            direcci�n IP de la m�quina donde se lanza la JVM
 * @param rmiPort
 *            puerto RMI utilizado para el RMIregistry OJO- Para asegurar la
 *            unicidad de los aids y de los servicios RMI, los admnistradores
 *            locales que corran en una misma maquina (cada uno lanzado en su
 *            propia JVM) DEBEN tener admName diferentes o rmiPort diferentes
 **/
@SuppressWarnings("all")
public class AdmBESA {

    private static AdmBESA admInstance; // variable para control tipo singleton
	/*
     * counterAgent contador que permite generar numeros unicos para los agentes
     * en cada administrador local
     */

    private String alias;
    private String ip;
    
    public static AdmBESA getInstance() {
        if (admInstance == null) {
            throw new UnsupportedOperationException("Todavia no se ha creado una instancia del admLocal");
        } else {
            return admInstance;
        }
    }
    /**
     * @uml.property name="counterAgent"
     */
    private int counterAgent = 0;
    /**
     * createInstance crear una instancia unica para el administrador local en
     * una JVM
     *
     * @param admAlias
     *            nombre/alias del administrador
     * @param passwd
     *            clave de seguridad - necesario para poder crear/destruir el
     *            adm
     * @param ipAddress
     *            es la direcci�n IP de la m�quina del que se registra
     * @param rmiPort
     *            es la direcci�n del registry de la m�quina del que se registra
     * @param multicastAddr
     *            direccion de multicast utilizada por los adm del mismo SMA
     * @param multicastPort
     *            puerto de multicast utilizado por los adm del mismo SMA
     * @return referencia a la instancia unica/singleton de adm local
     *
     */
    /**
     *Se incluy� flag para indicar si es centralizado o distribuido (18 de
     * Enero de 2006)
     */
    private GreenPagesHelper greenPagesHelper;

    public static AdmBESA createInstance(String admAlias, double passwd, String ipAddress, int rmiPort, String multicastAddr,
            int multicastPort, boolean centralized) throws Exception {
        if (admInstance == null) {
            if (!centralized) {
                admInstance = new AdmBESA(admAlias, passwd, ipAddress, rmiPort,
                        multicastAddr, multicastPort, centralized);
            } else {
                admInstance = new AdmBESA(admAlias, passwd, ipAddress, rmiPort,
                        centralized);
            }
        }
        return admInstance;
    }

    public static AdmBESA createInstance(String admAlias, double passwd,
            String ipAddress, int rmiPort, String multicastAddr,
            int multicastPort, int gcc_port) throws Exception {
        if (admInstance == null) {
            admInstance = new AdmBESA(admAlias, passwd, ipAddress, rmiPort,
                    multicastAddr, multicastPort, gcc_port, false);
        }
        return admInstance;
    }

    /**
     * ........................................................................
     * ... .....................................................................
     * ... .....................................
     */
    /**
     * Nueva constructora para versi�n centralizada (19 de Enero de 2006)
     */
    private AdmBESA(String admAlias, double passwd, String ipRmiRegistry,
            int portRmiRegistry, boolean centralized) throws ExceptionBESA {
        String messageException = new String("");

        try {
            greenPagesHelper = new GreenPagesHelper();
            this.centralized = centralized;
            if (this.centralized) {
                this.config = new ConfigBESA();
                messageException = new String(
                        ExceptionBESA_FatalError.EXCEPTION_ADMHANDLER);

                this.admIdTable = new Hashtable();
                this.admAliasTable = new Hashtable();
                this.agIdTable = new Hashtable();
                this.agAliasTable = new Hashtable();
                this.servIdTable = new Hashtable();
                this.passwd = passwd;
                this.alive = true;

                this.admHandler = new AdmHandler(admAlias, ipRmiRegistry,
                        portRmiRegistry);

            } else {
                ReportBESA.error("Can't initialize Distributed BESA because centralized BESA has been invoked");
            }
        } catch (Exception e) {
            ReportBESA.error("[AdmBESA::Constructor] " + e.getMessage());
        }
    }

    /**
     * AdmBESA constructora privada que crear una instancia de administrador
     * local
     *
     * @param admAlias
     *            nombre/alias del administrador
     * @param passwd
     *            clave de seguridad - necesario para poder crear/destruir el
     *            adm
     * @param ipAddress
     *            es la direcci�n IP de la m�quina del que se registra
     * @param rmiPort
     *            es la direcci�n del registry de la m�quina del que se registra
     * @param multicastAddr
     *            direccion de multicast utilizada por los adm del mismo SMA
     * @param multicastPort
     *            puerto de multicast utilizado por los adm del mismo SMA
     */
    private AdmBESA(String admAlias, double passwd, String ipRmiRegistry,
            int portRmiRegistry, String multicastAddr, int multicastPort,
            boolean centralized) throws ExceptionBESA {
        String messageException = new String("");

        try {
        	this.ip = ipRmiRegistry;
        	this.alias = admAlias;
            greenPagesHelper = new GreenPagesHelper();
            this.centralized = centralized;
            this.config = new ConfigBESA();
            messageException = new String(
                    ExceptionBESA_FatalError.EXCEPTION_ADMHANDLER);

            this.admIdTable = new Hashtable();
            this.admAliasTable = new Hashtable();
            this.agIdTable = new Hashtable();
            this.agAliasTable = new Hashtable();
            this.servIdTable = new Hashtable();
            this.passwd = passwd;
            this.alive = true;
            this.admHandler = new AdmHandler(admAlias, ipRmiRegistry,
                    portRmiRegistry);
            messageException = new String(
                    ExceptionBESA_FatalError.EXCEPTION_MULTICAST);
            if (!this.centralized) {
                new DistributedBESAInit(this, this.admHandler, portRmiRegistry,
                        multicastAddr, multicastPort);
            }

        } catch (Exception ioe) {
            throw new ExceptionBESA(messageException);
        }

    }

    private AdmBESA(String admAlias, double passwd, String ipRmiRegistry,
            int portRmiRegistry, String multicastAddr, int multicastPort, int GCC_Port,
            boolean centralized) throws ExceptionBESA {
        String messageException = new String("");

        try {
        	this.ip = ipRmiRegistry;
        	this.alias = admAlias;
            greenPagesHelper = new GreenPagesHelper();
            this.centralized = centralized;
            this.config = new ConfigBESA();
            messageException = new String(
                    ExceptionBESA_FatalError.EXCEPTION_ADMHANDLER);

            this.admIdTable = new Hashtable();
            this.admAliasTable = new Hashtable();
            this.agIdTable = new Hashtable();
            this.agAliasTable = new Hashtable();
            this.servIdTable = new Hashtable();
            this.passwd = passwd;
            this.alive = true;
            this.admHandler = new AdmHandler(admAlias, ipRmiRegistry,
                    portRmiRegistry);
            messageException = new String(
                    ExceptionBESA_FatalError.EXCEPTION_MULTICAST);


         //   this.initGCAFacades();
            new DistributedBESAInit(this, this.admHandler, portRmiRegistry,
                    multicastAddr, multicastPort, GCC_Port);
        } catch (Exception ioe) {
            throw new ExceptionBESA(messageException);
        }

    }

    public String getAlias() {
		return alias;
	}

	private void initGCAFacades() {
        this.clientGCA = GCAUtilities.getClientFacade();
//    	this.gccGCA = GCAUtilities.getGCCFacade();
    }

    /**
     * registerAdm registrar un administrador remoto presente en otra JVM - si
     * no esta registrado se crea el handler y se hace el lookup
     *
     * @param admAlias
     *            nombre/alias del administrador
     * @param ipAddress
     *            es la direcci�n IP de la m�quina del que se registra
     * @param rmiPort
     *            es la direcci�n del registry de la m�quina del que se registra
     * @param rmiName
     *            nombre del bind RMI, sirve como password para poder acceder a
     *            los servicios del AdmRemote que se esta registrando
     * @return null si hay problema -> excepcion en lookup || ya existe otro
     *         administrador local con el mismo alias y diferente nombre RMI
     *         (posible ambiguedad por nombres iguales)
     */
    public AdmRemoteHandler registerRemoteAdm(String admAlias,
            String ipRmiRegistry, int portRmiRegistry) throws Exception {
        // Verificar si no hay un adm con el mismo alias
        // Validar contra el adm local
        String admId = admAlias + AdmHandler.ID_DELIMITER + ipRmiRegistry + AdmHandler.ID_DELIMITER + Integer.toString(portRmiRegistry);
        if (admId.equalsIgnoreCase(this.admHandler.getAdmId())) {
            // Alias igual al del local -> No permitido o es un Eco de si mismo
            return null;
        }
        // Validar contra los de la tabla de adm remotos
        AdmRemoteHandler adm = (AdmRemoteHandler) admIdTable.get(admId);
        if (adm == null) {
            // Alias no existe -> Crear AdmHandler y meterlo a la tabla
            AdmRemoteHandler admHandler = new AdmRemoteHandler(admAlias,
                    ipRmiRegistry, portRmiRegistry);
            this.admIdTable.put(admId, admHandler);
            this.admAliasTable.put(admAlias, admHandler);
            return admHandler;
        } else {
            // Existe un administrador con el mismo alias -> validar rmiName
            return adm;
        }
    }

    /**
     * registerAdm registrar un administrador remoto presente en otra JVM - si
     * no esta registrado se crea el handler y se hace el lookup
     *
     * @param admAlias
     *            nombre/alias del administrador
     * @return null si hay problema -> excepcion en lookup || ya existe otro
     *         administrador local con el mismo alias y diferente nombre RMI
     *         (posible ambiguedad por nombres iguales)
     */
    public AdmHandler getAdmByAlias(String admAlias) throws Exception {
        // Verificar si no hay un adm con el mismo alias
        // Validar contra el adm local

        if (admAlias.equalsIgnoreCase(this.admHandler.getAlias())) {
            return this.admHandler;
        }
        // Buscar contra los de la tabla de adm remotos
        AdmRemoteHandler adm = (AdmRemoteHandler) admAliasTable.get(admAlias);
        return adm;
    }

    /**
     * registerAdm registrar un administrador remoto presente en otra JVM - si
     * no esta registrado se crea el handler y se hace el lookup
     *
     * @param admId
     *            Identificador del administrador
     * @return null si hay problema -> excepcion en lookup || ya existe otro
     *         administrador local con el mismo alias y diferente nombre RMI
     *         (posible ambiguedad por nombres iguales)
     */
    public AdmHandler getAdmById(String admId) throws Exception {
        // Verificar si no hay un adm con el mismo alias
        // Validar contra el adm local

        if (admId.equalsIgnoreCase(this.admHandler.getAdmId())) {
            return this.admHandler;
        }
        // Validar contra los de la tabla de adm remotos
        AdmRemoteHandler adm = (AdmRemoteHandler) admIdTable.get(admId);
        return adm;
    }

    public String getAliasAdmById(String admId) throws Exception {
        // Verificar si no hay un adm con el mismo alias
        // Validar contra el adm local

        // Validar contra los de la tabla de adm remotos
        AdmRemoteHandler adm = (AdmRemoteHandler) admIdTable.get(admId);
        return adm.getAlias();
    }

    public void unregisterRemoteAdm(String admAlias) {
        servIdTable.remove(admAlias);
    }

    public synchronized void moveAgent(String agId,
            String aliasDestinationAdmBESA, double passwdAgent)
            throws Exception {
        AgHandler agh = (AgHandler) this.getHandlerByAid(agId);
        if (!this.getAdmHandler().getAlias().equals(aliasDestinationAdmBESA)) {
            switch (agh.getAgHandlerBESA().getLocation()) {
                case (AgHandlerBESA.AG_LOCATION_LOCAL):
                    AgentBESA ag = agh.getAgHandlerBESA().getAg();
                    String nameClassAgent = ag.getClass().getName();
                    if (ag != null && ag.verifyPasswd(passwdAgent)) {
                        // Actualiza el estado a..en movimiento
                        // Cambio de filosofia, se bloquea m�s que el local cuando
                        // se mueva, los handlers remotos
                        // para que no envien eventos a un agente que se va a mover.
                        ReportBESA.debug(4,
                                "[AdmBESA::moveAgent()] Bloquea handlers debido a movimiento");
                        // Este no sirve debido a que cuando sube al otro lado el
                        // estado es live
                        // agh.getAgHandlerBESA().setStateAgent(AgHandlerBESA.AG_STATE_MOVE);
                        //

                        // Se hace un ping para avisar a todos los contenedores
                        // this.ping.updateStateAgent(AdmBESA.MSG_MULTICAST_AGENT_MOVE
                        // ,agId);

                        // Aviso a todos los Handler que este agente esta en
                        // movimiento
                        // pba
                        // this.ping.updateAgentbyMulticastMove(AdmBESA.MSG_MULTICAST_AGENT_MOVE
                        // ,agId, aliasAdmR);
                        // Esperar
                        // ag.waitChannel();
                        ag.move(passwdAgent);
                        // Invocar movimiento sobre contenedor destino
                        AdmRemoteHandler remoteAdmHandler = (AdmRemoteHandler) (this.getAdmByAlias(aliasDestinationAdmBESA));
                        // Fin del agente entonces desregistrar de los directorios e
                        // informa a los otros contenedores - evitar recibir
                        // mensajes
                        // Informar de cambio en los handlers a donde se mueve pasa
                        // de Remote a Local
                        remoteAdmHandler.getAdmRemote().moveAgentReceive(
                                ag.getAid(), ag.getState(), ag.getStructAgent(),
                                passwdAgent, nameClassAgent);
                        // Informar de cambio de ubicaci�n y actualiza los handlers,
                        // adem�s el handler local se vuelve remoto
                        this.unregisterAgent(agId, remoteAdmHandler,
                                aliasDestinationAdmBESA);
                        // agh.notifyAll();
                        // Se hace un ping para avisar a todos los contenedores que
                        // ya termino el movimiento del agente
                        // this.ping.updateStateAgent(AdmBESA.MSG_MULTICAST_SET_STATE_AGENT_MOVE
                        // ,agId);

                        ReportBESA.debug(4,
                                "[AdmBESA::moveAgent()] Termina Bloqueo de  handlers debido a movimiento");

                        // Se hace un ping para avisar a todos los contenedores
                        // this.ping.updateStateAgent(AdmBESA.MSG_MULTICAST_SET_STATE_AGENT_ALIVE,agId);

                    }
                    break;
                case (AgHandlerBESA.AG_LOCATION_REMOTE):
                    AdmRemoteHandler remoteAdmHandler = agh.getAgHandlerBESA().getAdm();
                    remoteAdmHandler.getAdmRemote().moveAgentSend(agId,
                            aliasDestinationAdmBESA, passwdAgent);
                    break;
            }
        }
    }

    /**
     * Este metodo retorna el id del Contenedor
     *
     * */
    public String getIdAdm() {
        return this.admHandler.getAdmId();
    }

    // ASI EA LOCAL O REMOTO SIEMPRE SE DEBERIA GUARDAR EN LOS DIRECTORIOS UN
    // AGHANDLER
    // DESPUES DE ESTO DEBERIA INSTANCIARSE A AGNESAHANDLER PARA SABER
    // INTERNAMENTE QUE Y CUAL DE VERDAD
    // HANDLER DEBERIA USAR, AL TIEMPO ESTA ES LA CLASE QUE DEBERIA ACTUALIZARSE
    // EL METODO DE ACTUALIZACION
    // EN ESTA MISMA CLASE SE INVOCA INTERNAMENTE Y DEPENDE DEL HANDLER
    // VERDADERO EL SENDEVENT, PERO
    // DEBIDO A LA DIVISION QUE SE HIZO SE TORNA TRANSPARENTE AL CLIENTE...YA NO
    // EXISTEN CLASES ABASTRACTAS.
    // YA LOCAL O REMOTO DEBERIA SE RMANEJO INTERNA PARA VER COMO ELABORA EL
    // SEND.
    /**
     * registerAgent registrar un nuevo agente presente en el sistema local
     *
     * @param ag
     *            referencia del agente que se registra
     * @param alias
     *            nombre con el que se podra referenciar al agente en paginas
     *            blancas
     * @return id �nico y global del agente
     **/
    public synchronized String registerLocalAgent(AgentBESA ag, String alias,
            boolean indMove, String aliasrr) {
        String aliasr = null;
        aliasr = alias;
        // Verificar si el agente ya esta registrado
        // Si es el caso devolver el mismo aid
        if (ag.getAid() == null) {
            // Crear tarjeta/aidHandler y adicionarla a las tablas de agentes
            String agId = null;
            // String agId = this.generateAgId(alias);
            if (indMove == false) {
                agId = this.generateAgId(aliasr);
                // SE QUITA AgLocalHandler aidHandler = new
                // AgLocalHandler(alias,agId,ag);
                AgHandler aidHandler = new AgHandler(aliasr, agId, ag,
                        AgHandlerBESA.AG_LOCATION_LOCAL);
                // SE ADICIONA INFO DE LOCAL
                agIdTable.put(agId, aidHandler);
                agAliasTable.put(aliasr, aidHandler);
                if(VisualSetUp.VISUAL){
                	ClientController.getInstance().addNewAgent(aliasr);
                }
            } else {
                agId = aliasr;
                aliasr = aliasrr;
                AgHandler agHandler = this.getHandlerByAid(agId);
                agHandler.createReferenceLocalHandler(ag);
            }
            return agId;
        } else {
            return ag.getAid();
        }
    }

    // Se avisa a todos los contenedores de la existencia de un nuevo agente
    // Se adiciona que si ya estaba creado en este contenedor pero como remoto
    // entonces no lo publica
    public synchronized void publicagent(String alias, String agId) {
        this.ping.updateAgentbyMulticast(AdmBESA.MSG_MULTICAST_AGENT_CREATE,
                agId);
    }

    public synchronized void registerRemoteAgent(String agAlias, String agId,
            String admId) {
        // Verificar si el agente ya esta registrado
		/*
         * -- AgRemoteHandler agh = (AgRemoteHandler)this.agIdTable.get(agId);
         * if (agh != null) { // Si ya existe -> actualizar los datos del handle
         * agh.update((AdmRemoteHandler)this.admIdTable.get(admId)); } else { --
         */
        // Crear nuevo handle remoto e incluirlo en las tablas
        // -- agh = new AgRemoteHandler (agAlias, agId,
        // (AdmRemoteHandler)this.admIdTable.get(admId));

        AgHandler agh = new AgHandler((AdmRemoteHandler) this.admIdTable.get(admId), agAlias, agId, AgHandlerBESA.AG_LOCATION_REMOTE);

        this.agIdTable.put(agId, agh);
        this.agAliasTable.put(agAlias, agh);
        if(VisualSetUp.VISUAL){
        	ClientController.getInstance().addNewAgent(agAlias);
        }
        // -- }
    }

    private synchronized String generateAgId(String alias) {
        // formato del aid -> 1.alias 2.ipAddress 3.rmiPort 4.counterLocal
        return this.admHandler.getAdmId() + AdmHandler.ID_DELIMITER + Integer.toString(++counterAgent);
    }

    // Des-registrar para matar un agente
    public synchronized void unregisterAgent(String agId) {
        // AgHandler referenceAgent;
        // Purgar tabla de servicios
        for (Enumeration enuma = servIdTable.keys(); enuma.hasMoreElements();) {
            Service serv = (Service) servIdTable.get(enuma.nextElement());
            serv.removeAgent(agId);
        }
        //Purgar tabla páginas verdes
        this.greenPagesHelper.unregisterAgent(agId);
        // Se elimina la referencia en los otros contenedores
        this.ping.updateAgentbyMulticast(AdmBESA.MSG_MULTICAST_AGENT_DESTROY,
                agId);
        // Eliminar agente de tabla de agentes
        agAliasTable.remove(getAliasByAid(agId));
        agIdTable.remove(agId);
    }

    /**
     * unregisterAgent sacar de las tablas de registro un agente local
     *
     * @param agId
     *            id del agente a sacar
     **/
    public synchronized void unregisterAgent(String agId,
            AdmRemoteHandler admRH, String aliasAdmR) {
        // AgHandler referenceAgent;
        // Purgar tabla de servicios
        for (Enumeration enuma = servIdTable.keys(); enuma.hasMoreElements();) {
            Service serv = (Service) servIdTable.get(enuma.nextElement());
            serv.removeAgent(agId);
        }
        //Purgar tabla páginas verdes
        this.greenPagesHelper.unregisterAgent(agId);
        // Se elimina la referencia en los otros contenedores
        this.ping.updateAgentbyMulticastMove(AdmBESA.MSG_MULTICAST_AGENT_MOVE,
                agId, aliasAdmR);
        AgHandler ahp = this.getHandlerByAid(agId);
        // Actualizar Handler de local a remoto
        ahp.createReferenceRemoteHandler(admRH);
    }

    /** Se elimina las referencias de una agente remoto de las tabals de agnetes */
    /** en este paso se debe actualizar el handler remoto */
    public void unregisterRemoteAgent(String agId) {
        // Eliminar agente de tabla de agentes
        agAliasTable.remove(getAliasByAid(agId));
        agIdTable.remove(agId);
        //Purgar tabla páginas verdes
        this.greenPagesHelper.unregisterAgent(agId);
    }

    /** Se elimina las referencias de una agente remoto de las tabals de agnetes */
    /** en este paso se debe actualizar el handler remoto */
    // public void updateUnregisterRemoteAgent(String agId, AdmRemoteHandler
    // admRH) {
    public void updateUnregisterRemoteAgent(String agAlias,
            String aliasAdmRemote) {
        try {
            // Eliminar agente de tabla de agentes
            // AgHandler ahp = this.getHandlerByAid(agId);
            AgHandler ahp = this.getHandlerByAlias(agAlias);
            if (ahp.getAgHandlerBESA().getLocation() != AgHandlerBESA.AG_LOCATION_LOCAL) {
                AdmRemoteHandler admRH = (AdmRemoteHandler) this.getAdmByAlias(aliasAdmRemote);
                ahp.getAgHandlerBESA().setAdmRemoteHandler(admRH);
            }
        } catch (Exception ioe) {
            System.out.println("Error:" + ioe);
        }
        /*
         * agAliasTable.remove(getAliasByAid(agId)); agIdTable.remove(agId);
         */
    }

    /**
     * searchAidByAlias buscar agentes que responden a un alias - paginas
     * blancas
     *
     * @param alias
     *            nombre con el que se referencian el/los agentes buscados
     * @return un iterator con los aids de los agentes encontrados OJO: por
     *         ahora retorna solo el aid de un agente, falta hacer lo del
     *         iterator
     **/
    public synchronized String searchAidByAlias(String alias)
            throws ExceptionBESA {
        // Buscar el aid del agente usando el alias
        String id = new String();
        int i;
        for (i = 0; i < 10; i++) { // 10 sg. m�ximo de espera para el registro
            try {
                AgHandler agh = (AgHandler) agAliasTable.get(alias);
                if (agh != null) {
                    id = agh.getAgId();
                    i = 11;
                }
            } catch (Exception e) {
                try {
                    Thread.sleep(3000);
                } catch (Exception err) {
                    throw new ExceptionBESA(
                            ExceptionBESA_AgentNotFound.EXCEPTION_ALIAS + alias);
                }
            }
        }
        if (i == 11) {
            throw new ExceptionBESA(ExceptionBESA_AgentNotFound.EXCEPTION_ALIAS + alias);
        }
        return id;
        // return ((AgHandler)agAliasTable.get(alias)).getAgId();
    }

    /**
     * searchAidByService buscar agentes que prestan un servicio - paginas
     * amarillas
     *
     * @param servId
     *            nombre/identificador del servicio que prestan el/los agentes
     *            buscados
     * @return un iterator con los ids de los agentes encontrados
     **/
    /*
     * public synchronized Iterator searchAidByService(String servId) { //
     * Tratar de obtener referencia al servicio a partir del nombre Service serv
     * = (Service)servIdTable.get(servId); // Verificar si el servicio no ha
     * sido creado if (serv == null) { // Retornar null para indicar que el
     * servicio no existe return null; } else { // Sino, hacer copia del vector
     * de agentes y retornar el iterator Vector servCopy =
     * (Vector)serv.getAgIds().clone(); return servCopy.iterator(); } }
     */
    /**
     * searchAliasByAid obtener el alias de un agente a partir del aid
     *
     * @param agId
     *            id del agente
     * @return alias del agente
     **/
    public String getAliasByAid(String agId) {
        return ((AgHandler) agIdTable.get(agId)).getAlias();
    }

    /**
     *
     * getHandlerByAid obtener el aidHandler de un agente a partir del aid
     *
     * @param agId
     *            id del agente
     * @return aidHandler del agente
     **/
    public AgHandler getHandlerByAid(String agId) {
        return (AgHandler) agIdTable.get(agId);
    }

    /**
     * getHandlerByAid obtener el aidHandler de un agente a partir del aid
     *
     * @param agId
     *            id del agente
     * @return aidHandler del agente
     **/
    public AgHandler getHandlerByAlias(String agAlias) {
        return (AgHandler) agAliasTable.get(agAlias);
    }

    // Para borrar la referencia
    // retorna el alias, util para registrar el mismo debido a que por
    // reflection se envia el aid
    public String erase(String aid) {
        AgHandler agH = this.getHandlerByAid(aid);
        /*
         * agAliasTable.remove(agH.getAlias()); agIdTable.remove(agH.getAgId());
         * NO SE PUEDE BORRAR SE DEBE ACTUALIZAR
         */
        return agH.getAlias();
    }

    /**
     * addService crear un servicio en las paginas amarillas
     *
     * @param servId
     *            nombre/identificador unico del servicio
     * @param descriptors
     *            vector con nombres alternativos/descriptivos del servicio
     **/
    public synchronized void addService(String servId) {
        // Tratar de obtener referencia al servicio a partir del nombre
        Service serv = (Service) servIdTable.get(servId);
        // Verificar si el servicio no ha sido creado
        if (serv == null) {
            // Crear servicio y meterlo a la tabla
            serv = new Service(servId);
            servIdTable.put(servId, serv);
        }
    }

    /**
     * addService crear un servicio en las paginas amarillas
     *
     * @param servId
     *            nombre/identificador unico del servicio
     * @param descriptors
     *            vector con nombres alternativos/descriptivos del servicio
     **/
    public synchronized void addService(String servId, ArrayList descriptors) {
        // Tratar de obtener referencia al servicio a partir del nombre
        Service serv = (Service) servIdTable.get(servId);
        // Verificar si el servicio no ha sido creado
        if (serv == null) {
            // Crear servicio y meterlo a la tabla
            serv = new Service(servId, descriptors);
            servIdTable.put(servId, serv);
        } else {
            // Sino, agregar solo los descriptores
            for (int i = 0; i < descriptors.size(); i++) {
                serv.addDescriptor((String) descriptors.get(i));
            }
        }
    }

    /**
     * removeService eliminar un servicio en las paginas amarillas
     *
     * @param servId
     *            nombre/identificador unico del servicio
     **/
    public void removeService(String servId) {
        // Tratar de obtener referencia al servicio a partir del id
        Service serv = (Service) servIdTable.get(servId);
        // Verificar si el servicio no ha sido creado
        if (serv != null) {
            // Retirar servicio de la tabla
            servIdTable.remove(servId);
        }
    }

    /**
     * searchService buscar un servicio en las paginas amarillas
     *
     * @param descriptor
     *            nombre alternativo/descriptivo del servicio
     * @return nombre identificador unico del servicio
     **/
    /*
     * public synchronized String searchService(String descriptor) { // Obtener
     * y recorrer lista de servicios for (Enumeration list =
     * servIdTable.elements(); list.hasMoreElements(); ) { // Verificar si el
     * descriptor coorresponde con el solicitado Service elemen =
     * (Service)list.nextElement(); if
     * (elemen.getServId().equalsIgnoreCase(descriptor)) // Parametro descriptor
     * coincide con el id del servicio return elemen.getServId(); if
     * (elemen.searchDescriptor(descriptor)) // Retornar el nombre del servicio
     * asociado al descriptor return elemen.getServId(); } return null; }
     */
    /**
     * bindService asociar un agente a un servicio de las paginas amarillas
     *
     * @param agId
     *            id del agente
     * @param servId
     *            nombre/identificador unico del servicio
     * @return true si todo bien, false si el servicio no ha sido creado
     **/
    public synchronized boolean bindService(String agId, String servId) {
        // Tratar de obtener referencia al servicio a partir del nombre
        Service serv = (Service) servIdTable.get(servId);
        // Verificar si el servicio no ha sido creado
        if (serv == null) {
            // Retornar falso
            return false;
        } else {
            // Sino, incluir agente en la lista de agentes asociados al servicio
            serv.addAgent(agId);
            return true;
        }
    }

    /**
     * unbindService eliminar asociacion de un agente a un servicio de las
     * paginas amarillas
     *
     * @param agId
     *            id del agente
     * @param servId
     *            nombre/identificador unico del servicio
     * @return true si todo bien, false si el servicio no ha sido creado
     **/
    public void unbindService(String agId, String servId) {
        // Tratar de obtener referencia al servicio a partir del nombre
        Service serv = (Service) servIdTable.get(servId);
        // Verificar si el servicio no ha sido creado
        if (serv != null) {
            // Eliminar agente en la lista de agentes asociados al servicio
            serv.removeAgent(agId);
        }
    }

    /**
     * kill cerrar el administrador y con el todos los agentes e hilos locales
     *
     * @param passwd
     *            clave de acceso a este metodo protegido
     **/
    public void kill(double passwd) {
        if (this.passwd == passwd) {
            ReportBESA.debug(10,
                    "[AdmBESA::kill()] Password Correcto - Iniciando cierre de Threads");
            // avisar muerte a los otros administradores usando el hilo ping
            // detener hilos de multicast cambiando alive a false
            this.setAlive(false);
            // detener todos los agentes locales
            ReportBESA.debug(10,
                    "[AdmBESA::kill()] Password Correcto - Cierre de Threads Concluido");
        }
    }

    /**
     * @return Returns the admHandler.
     * @uml.property name="admHandler"
     */
    public AdmHandler getAdmHandler() {
        return admHandler;
    }

    /**
     * @param admHandler
     *            The admHandler to set.
     * @uml.property name="admHandler"
     */
    public void setAdmHandler(AdmHandler admHandler) {
        this.admHandler = admHandler;
    }

    /**
     * @return Returns the multicastPort.
     * @uml.property name="multicastPort"
     */
    public int getMulticastPort() {
        return this.multicastPort;
    }

    /**
     * 18 - Ene - 2006
     *
     * @uml.property name="multicastPort"
     */
    public void setMulticastPort(int port) {
        this.multicastPort = port;
    }

    /**
     * @return Returns the multicastInetAddr.
     * @uml.property name="multicastInetAddr"
     */
    public InetAddress getMulticastInetAddr() {
        return this.multicastInetAddr;
    }

    /**
     * 18 - Ene - 2006
     *
     * @uml.property name="multicastInetAddr"
     */
    public void setMulticastInetAddr(InetAddress addr) {
        this.multicastInetAddr = addr;
    }

    /**
     * @return Returns the multicastSocket.
     * @uml.property name="multicastSocket"
     */
    public MulticastSocket getMulticastSocket() {
        return this.multicastSocket;
    }

    /**
     * 18 - Ene - 2006
     *
     * @uml.property name="multicastSocket"
     */
    public void setMulticastSocket(MulticastSocket ms) {
        this.multicastSocket = ms;
    }

    /**
     * 18 - Ene - 2006
     *
     * @uml.property name="ping"
     */
    public void setPing(PingThread ping) {
        this.ping = ping;
        this.greenPagesHelper.setPing(ping);
    }

    public GreenPagesHelper getGreenPagesHelper() {
        return greenPagesHelper;
    }

    /**
     * 18 - Ene - 2006
     *
     * @uml.property name="pong"
     */
    public void setPong(PongThread pong) {
        this.pong = pong;
    }

    /**
     * 18 - Ene - 2006
     *
     * @uml.property name="ping"
     */
    public PingThread getPing() {
        return this.ping;
    }

    /**
     * 18 - Ene - 2006
     *
     * @uml.property name="pong"
     */
    public PongThread getPong() {
        return this.pong;
    }

    final public synchronized boolean isAlive() {
        return alive;
    }

    /**
     * @param alive
     *            The alive to set.
     * @uml.property name="alive"
     */
    private synchronized void setAlive(boolean alive) {
        this.alive = alive;
    }

    /**
     * @return Returns the agIdTable.
     * @uml.property name="agIdTable"
     */
    public Hashtable getAgIdTable() {
        return this.agIdTable;
    }

    /**
     * @return Returns the admIdTable.
     * @uml.property name="admIdTable"
     */
    public Hashtable getAdmIdTable() {
        return this.admIdTable;
    }

    public void geLocalDirectoryList(ArrayList idList, ArrayList aliasList) {
        Enumeration keys = this.getAgIdTable().keys();

        while (keys.hasMoreElements()) {
            // quito AgHandler agh =
            // ((AgHandlerBESA)(this.getAgIdTable().get(keys.nextElement()))).obtainAgLocal();
            AgHandler agh = ((AgHandler) (this.getAgIdTable().get(keys.nextElement()))).obtainAgLocal();

            if (agh != null) {
                idList.add(agh.getAgId());
                aliasList.add(agh.getAlias());
            }
        }
    }

    public void updateAgentDirectory(String admId, ArrayList agIdList,
            ArrayList aliasList) {
        while (!agIdList.isEmpty()) {
            String agId = (String) agIdList.remove(0);
            String agAlias = (String) aliasList.remove(0);
            this.registerRemoteAgent(agAlias, agId, admId);
        }
    }

    public void updateStateAgent(String msgType, String agId) {
        // 0. msgType y 1. agId
        AgHandler agh = this.getHandlerByAid(agId);
        // Se actualiza en todos los contenedores el estado a move o kill, la
        // idea es evitar
        // mientras se mueve que le envien eventos
        if (msgType.equalsIgnoreCase(AdmBESA.MSG_MULTICAST_SET_STATE_AGENT_MOVE)) {
            agh.getAgHandlerBESA().setStateAgent(AgHandlerBESA.AG_STATE_MOVE);
        } else if (msgType.equalsIgnoreCase(AdmBESA.MSG_MULTICAST_AGENT_DESTROY)) {
            agh.getAgHandlerBESA().setStateAgent(AgHandlerBESA.AG_STATE_KILL);
        } else if (msgType.equalsIgnoreCase(AdmBESA.MSG_MULTICAST_SET_STATE_AGENT_ALIVE)) {
            agh.getAgHandlerBESA().setStateAgent(AgHandlerBESA.AG_STATE_ALIVE);
        }
    }

    /**
     * @return Returns the passwd.
     * @uml.property name="passwd"
     */
    public double getPasswd() {
        return this.passwd;
    }

    public boolean verifyPasswd(double passwd) {
        if (this.passwd == passwd) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Metodo que retorna el flag de centralizado o distribuido (19 de Enero de
     * 2006)
     */
    public boolean isCentralized() {
        return this.centralized;
    }

    /**
     * Metodo que retorna el configBESA (19 de Enero de 2006)
     */
    public ConfigBESA getConfigBESA() {
        return this.config;
    }

    // Solicitar muerte del agente
    public void killAgent(String agId, double passwdAgent) {
        AgHandler agh = (AgHandler) this.getHandlerByAid(agId);
        if (agh != null && agh.getAg().verifyPasswd(passwdAgent)) {
            switch (agh.getAgHandlerBESA().getLocation()) {
                case (AgHandlerBESA.AG_LOCATION_LOCAL):
                    agh.getAgHandlerBESA().setStateAgent(
                            AgHandlerBESA.AG_STATE_KILL);
                    agh.getAg().waitChannel();// Bloquea de forma logica el canal
                    agh.getAg().kill(passwdAgent);
                    this.unregisterAgent(agId);
                    break;
                case (AgHandlerBESA.AG_LOCATION_REMOTE):
                    // AdmRemoteHandler remoteAdmHandler =
                    // agh.getAgHandlerBESA().getAdm();
                    // remoteAdmHandler.getAdmRemote().moveAgentSend(agId,aliasDestinationAdmBESA,passwdAgent);
                    break;
            }
        }
    }

    // toca evaluar si el estado se elimina del agente.
    // Retorna el estado del agente
    public StateBESA getStateAgent(String aid) throws Exception {
        // se quita AgHandlerBESA agh =
        // (AgHandlerBESA)this.getHandlerByAid(aid);
        AgHandler agh = (AgHandler) this.getHandlerByAid(aid);
        // quita return ((AgLocalHandler)agh).getAg().getStateAgent();
        return ((AgHandler) agh).getAg().getStateAgent();
    }

    // Retorna la estructura del agente
    public StructBESA getStructAgent(String aid) throws Exception {
        // se quita AgHandlerBESA agh =
        // (AgHandlerBESA)this.getHandlerByAid(aid);

        AgHandler agh = (AgHandler) this.getHandlerByAid(aid);
        return ((AgHandler) agh).getAg().getStructAgent();

    }

    /*
     * multicastPort puerto para multicast
     */
    /**
     * @uml.property name="ping"
     * @uml.associationEnd inverse="admLocal:BESA.SystemLevel.PingThread"
     */
    private PingThread ping;

    /*
     * MulticastClientThread manejo de la recepcion y tratamiento de los
     * mensajes de multicast
     */
    /**
     * @uml.property name="pong"
     * @uml.associationEnd inverse="admLocal:BESA.SystemLevel.PongThread"
     */
    private PongThread pong;

    /*
     * admAliasTable tabla de administradores/AdmRemoteHandlers accesible por
     * idAdm
     */
    /**
     * @uml.property name="admIdTable"
     * @uml.associationEnd qualifier=
     *                     "admId:java.lang.String BESA.DistributedBESA.Directory.AdmRemoteHandler"
     */
    private Hashtable admIdTable;

    /*
     * admAliasTable tabla de administradores/AdmRemoteHandlers accesible por
     * alias
     */
    /**
     * @uml.property name="admAliasTable"
     * @uml.associationEnd qualifier=
     *                     "admAlias:java.lang.String BESA.DistributedBESA.Directory.AdmRemoteHandler"
     */
    private Hashtable admAliasTable;
    /**
     * @link aggregation
     * @associates <{aidHandler}>
     * @uml.property name="agIdTable"
     * @uml.associationEnd
     *                     qualifier="agId:java.lang.String BESA.SystemLevel.Directory.AgHandler"
     */
    /*
     * agIdTable tabla de agentes/aidHandlers accesible por aid
     */
    private Hashtable agIdTable;
    /**
     * @link aggregation
     * @associates <{aidHandler}>
     * @uml.property name="agAliasTable"
     * @uml.associationEnd qualifier=
     *                     "getAliasByAid:java.lang.String BESA.SystemLevel.Directory.AgHandler"
     */
    /*
     * agAliasTable tabla de agentes/aidHandlers accesible por alias
     */
    private Hashtable agAliasTable;
    /**
     * @link aggregation
     * @associates BESA.System.Directory.Service
     * @uml.property name="servIdTable"
     * @uml.associationEnd
     *                     qualifier="servId:java.lang.String BESA.SystemLevel.Directory.Service"
     */
    /*
     * servIdTable tabla de servicios accesible por el identificador del
     * servicio
     */
    private Hashtable servIdTable;

    /*
     * admHandler datos del host en el cual se lanza el administrador local
     */
    /**
     * @uml.property name="admHandler"
     * @uml.associationEnd
     */
    private AdmHandler admHandler;

    /*
     * passwd password que controla el acceso a metodos criticos del
     * administrador
     */
    /**
     * @uml.property name="passwd"
     */
    private double passwd;

    /*
     * alive indicador de si actividad del administrador, afecta la bucla
     * principal de los hilos de multicast
     */
    /**
     * @uml.property name="alive"
     */
    private boolean alive;

    /*
     * multicastPort puerto del socket para multicast
     */
    /**
     * @uml.property name="multicastPort"
     */
    private int multicastPort;

    /*
     * multicastSocket socket para multicast
     */
    /**
     * @uml.property name="multicastSocket"
     */
    private MulticastSocket multicastSocket;

    /*
     * multicastInetAddr direccion inet para multicast
     */
    /**
     * @uml.property name="multicastInetAddr"
     */
    private InetAddress multicastInetAddr;

    /*
     * MSG_MULTICAST_ALIVE constante para definir el tipo ALIVE del contenedor
     * en un mensaje de multicast
     */
    public static final String MSG_MULTICAST_ADM_ALIVE = "ALIVE";

    /*
     * MSG_MULTICAST_KILL constante para definir el tipo KILL del contenedor en
     * un mensaje de multicast
     */
    public static final String MSG_MULTICAST_ADM_KILL = "KILL";

    /*
     * MSG_MULTICAST_AGENT_CREATE constante para definir el tipo CREATE del
     * agente en un mensaje de multicast
     */
    public static final String MSG_MULTICAST_AGENT_CREATE = "CREATE";

    /*
     * MSG_MULTICAST_KILL constante para definir el tipo DESTROY del agente en
     * un mensaje de multicast
     */
    public static final String MSG_MULTICAST_AGENT_DESTROY = "DESTROY";

    /*
     * MULTICAST_DELIMITER constante para separar datos en un mensaje de
     * multicast
     */
    public static final String MULTICAST_DELIMITER = "%";

    /*
     * MULTICAST_DELIMITER constante para definir el tipo MOVER el agente en un
     * mensaje multicast
     */
    public static final String MSG_MULTICAST_AGENT_MOVE = "MOVE";

    /*
     * MULTICAST_DELIMITER constante para definir el tipo INFORMAR MOVER el
     * agente en un mensaje multicast
     */
    public static final String MSG_MULTICAST_SET_STATE_AGENT_MOVE = "SET_STATE_MOVE";

    /*
     * MULTICAST_DELIMITER constante para definir el tipo INFORMAR FIN MOVER el
     * agente en un mensaje multicast
     */
    public static final String MSG_MULTICAST_SET_STATE_AGENT_ALIVE = "SET_STATE_ALIVE";
    /**
     * Flag para indicar si es centralizado o distribuido (19 de Enero de 2006)
     *
     * @uml.property name="centralized"
     */
    private boolean centralized;
    /**
     * Clase de configuraci�n del sistema
     *
     * @uml.property name="config"
     * @uml.associationEnd
     */
    private ConfigBESA config;

    public void showDirectory() {
        System.out.println(" --- DIRS ----");
        Set keys = admIdTable.keySet();
        for (Iterator iterator = keys.iterator(); iterator.hasNext();) {
            Object type = (Object) iterator.next();
            System.out.println("\t\t" + type + " --- " + admIdTable.get(type));

        }
        System.out.println("------------------------");
        keys = admAliasTable.keySet();
        for (Iterator iterator = keys.iterator(); iterator.hasNext();) {
            Object type = (Object) iterator.next();
            System.out.println("\t\t" + type + " --- " + admAliasTable.get(type));

        }

    }
    //DISTRIBUTED VARIALES
    private GCC_Impl gcc;

    /**
     * @return the gcc
     */
    public GCC_Impl getGcc() {
        return gcc;
    }

    public void imprimirGCC() {
        gcc.imprimirLog();
    }

    public void setGCC(GCC_Impl gcc) {
        this.gcc = gcc;
    }

    private ClientGCA clientGCA;

    public long openTransaction() throws ExceptionBESA {
        try {
            return clientGCA.openTransaction();
        } catch (RemoteException ex) {
            Logger.getLogger(AdmBESA.class.getName()).log(Level.SEVERE, null, ex);
        }
        throw new ExceptionBESA("Transaction can't be open");
    }

    public boolean closeTransaction(long idTransaction) {
        try {
            return clientGCA.closeTransaction(idTransaction);
        } catch (RemoteException ex) {
            Logger.getLogger(AdmBESA.class.getName()).log(Level.SEVERE, null, ex);
        }
        return false;
    }

    public void setGCA(ClientGCA gca) {
        clientGCA = gca;
    }

	public String getIP() {
		return this.ip;
	}

	public Hashtable getAgAliasTable() {
		return agAliasTable;
		
	}
}
